"""
实现步骤:
    1. 创建套接字
    2. 等待接收客户端请求
    3. 客户端连接创建新的进程处理客户端请求
    4. 原进程继续等待其他客户端链接
    5. 如果客户端退出,则回收对应的进程
    6. 处理僵尸进程,signal系统托管
    

"""

from socket import *
import os
import signal  # 系统托管进程

# 全局变量
HOST = '0.0.0.0'
PORT = 18693
ADDR = (HOST, PORT)

# 创建套接字
s = socket()  # 默认tcp模式
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # 启用端口重用, SOL_SOCKET SO_REUSEADDR 允许重用本地地址和端口
s.bind(ADDR)  # 绑定ip端口
s.listen(5)  # 能同时链接几个客户端

def handle(c):
    while True:
        data = c.recv(1024) # 收取消息
        if not data:
            break
        print(f"客户端发送的消息: {data.decode()}")
        c.send('ok'.encode())
    c.close()

# 在实例化多进程之前实例化系统托管进程
signal.signal(signal.SIGCHLD, signal.SIG_IGN)

print("等待客户端连接, 端口: 18693...")

# 循环处理客户端连接
while True:
    try:
        c, addr = s.accept()
        print(f"有客户端连接:{addr}")
    except KeyboardInterrupt:  # ctrl + c 异常结束程序捕获
        os._exit(0)
    except Exception as e:  # 其他错误捕获,结束这一次循环
        print(e)
        continue

    # 父进程创建子进程处理客户端事件
    pid = os.fork()
    if pid == 0:
        s.close() # 子进程关闭tcp是不影响父进程的tcp的,他关的是自己的而不是父进程的
        handle(c) # 子进程事件
        os._exit(0)  # 处理完成事件,回收子进程
    else: # 父进程及pid创建失败时返回接收语句继续监听客户端连接情况
        c.close()  # 父进程不需要和客户端进行通讯,只关注pid和客户端连接情况
        